package world.snowcrystal.controller;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import world.snowcrystal.dao.UserAuthenticationDetails;
import world.snowcrystal.dto.ApplicationMessage;
import world.snowcrystal.dto.ChatroomMessage;
import world.snowcrystal.dto.ChatroomUser;
import world.snowcrystal.event.LogoutSuccessWithContextEvent;
import world.snowcrystal.exception.SnowcrystalAlreadyExistException;
import world.snowcrystal.properties.AuthenticationProperties;
import world.snowcrystal.properties.ChatroomProperties;
import world.snowcrystal.service.ChatroomService;
import world.snowcrystal.utils.JwtUtils;

import java.util.Collection;
import java.util.Map;
import java.util.Set;


@Log4j2
@Controller
@RequestMapping("/chatroom")
public class ChatroomController {

    @Autowired
    private SimpMessagingTemplate messagingTemplate;

    @Autowired
    private ChatroomProperties chatroomProperties;

    @Autowired
    private AuthenticationProperties authenticationProperties;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private ChatroomService chatroomService;

    public ChatroomController() {
    }

    @GetMapping(value = "/{channel}/info/history", params = {"n", "offset"})
    public ResponseEntity<ApplicationMessage<Collection<ChatroomMessage>>> retrieveRecentMessages(
            @RequestParam("n") Integer n,
            @RequestParam("offset") Integer offset
    ) {
        Collection<ChatroomMessage> recentMessages = chatroomService.getRecentMessages(offset, n);
        int messageCount = chatroomService.cachedMessageNumber();
        if ((n * offset) < messageCount) {
            return ResponseEntity.ok(ApplicationMessage.hasMore("ok", recentMessages));
        }
        return ResponseEntity.ok(ApplicationMessage.ok("ok", recentMessages));

    }

    @PostMapping("/{channel}/keep-alive")
    public ResponseEntity<ApplicationMessage<Void>>
    keepAlive(
            @RequestParam(value = "fingerprint", required = false) String pfp,
            @CookieValue(value = "fingerprint", required = false) String cfp) {

        String fp = StringUtils.hasText(cfp) ? cfp : pfp;
        if (!StringUtils.hasText(fp)) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                    .body(ApplicationMessage.badRequest("需要提供更多信息哦"));
        }


        if (chatroomService.checkDeviceExistence(fp)) {
            chatroomService.refreshUserExpiration(fp);
            return ResponseEntity.ok(ApplicationMessage.ok("ok"));
        }
        return ResponseEntity.ok(ApplicationMessage.notFound("not ok"));
    }


    @PostMapping("/{channel}/entrance")
    public ResponseEntity<ApplicationMessage<?>>
    chatroomUserRegistration(
            @RequestParam(value = "fingerprint", required = false) String pfp,
            @CookieValue(value = "fingerprint", required = false) String cfp,
            HttpServletRequest request) {
        String fp = StringUtils.hasText(cfp) ? cfp : pfp;
        ChatroomUser chatroomUser = null;
        boolean absent = !chatroomService.checkDeviceExistence(fp);;
        if (!StringUtils.hasText(fp)) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                    .body(ApplicationMessage.badRequest("需要提供更多信息哦"));
        }


        // try to enter with token.
        Cookie[] cookies = request.getCookies();
        String token = "";
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (authenticationProperties.getRememberMeCookieName().equals(cookie.getName())) {
                    token = cookie.getValue();
                }
            }
        }
        if (StringUtils.hasText(token)) {
            try {

                Map<String, String> stringStringMap = JwtUtils.verifyJWT(token);
                UserAuthenticationDetails userDetails = (UserAuthenticationDetails) userDetailsService.loadUserByUsername(stringStringMap.get("username"));
                chatroomUser = ChatroomUser.createAuthedUser(fp, userDetails.getId(), userDetails.getUsername());
                chatroomService.addOnlineChatroomUser(chatroomUser, true);
                log.info("user {} entered chatroom", chatroomUser.getUsername());
                if(absent){
                    broadcastMessage(ChatroomMessage.enter(chatroomUser.getUsername()));
                }
                return ResponseEntity.ok(ApplicationMessage.useNative("native"));
            } catch (Exception doNothing) {
                //TODO Vulnerability
                chatroomUser = null;
            }
        }

        chatroomUser = chatroomService.getChatroomUser(fp);
        if (chatroomUser != null) {
            if (isAnonymousUser(chatroomUser)) {
                chatroomService.refreshUserExpiration(fp);
                return ResponseEntity.ok(ApplicationMessage.created("",chatroomUser));
            }
        }


        // try to generate anonymous representation.
        String anonyId = chatroomService.generateChatroomUserId();
        chatroomUser = ChatroomUser.createAnonymousUser(fp, anonyId,
                chatroomProperties.getChatroomAnonymousUserPrefix()
                        + anonyId.substring(anonyId.length() - 5));
        try {
            chatroomService.addOnlineChatroomUser(chatroomUser,true);
            log.info("user {} entered chatroom", chatroomUser.getUsername());
        } catch (SnowcrystalAlreadyExistException e) {
            chatroomService.refreshUserExpiration(fp);
            log.info("user {} reentered chatroom", chatroomUser.getUsername());
        }
        return ResponseEntity.ok(ApplicationMessage.created("created", chatroomUser));
    }


    @MessageMapping("/message")
    public ChatroomMessage message(@Payload ChatroomMessage message) {
        log.info("chatroom: {} : {}", message.getUsername(), message.getMessage());
        messagingTemplate.convertAndSend("/channel/message", message);
        log.info("try cache the message");
        if (chatroomService.addNewMessage(message)) {
            log.info("new message added");
        } else {
            log.info("failed to add new message to cache");
        }
        return message;
    }


    @EventListener
    public void onLogout(LogoutSuccessWithContextEvent event){
        final String fingerprint = event.getFingerprint();
        chatroomService.deleteChatroomUser(fingerprint);
    }

    public void broadcastMessage(@Payload ChatroomMessage message) {
        messagingTemplate.convertAndSend("/channel/message", message);
        log.info("broadcast message added");
    }

    private boolean isAnonymousUser(ChatroomUser user){
        return user.getId().startsWith("#");
    }



}
